using System;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using Autofac;
using Fiddler;
using LocalOverride.Fiddler.Store;
using LocalOverride.Fiddler.UI;
using FiddlerApp = Fiddler;
using LocalOverride.Fiddler.UrlMapping;
using System.Collections.Generic;

[assembly: FiddlerApp.RequiredVersion("2.2.8.6")]

namespace LocalOverride.Fiddler
{
    public class LocalOverrideAutoTamper : IAutoTamper
    {
        private TabPage oPage;
        private IContainer container;
        private MappingRegistryFactory mappingRegistryFactory;
        private ILogger logger;
        public LocalOverrideAutoTamper()
        {
            container = ComponentBuilder.SetupAutoFac();
            mappingRegistryFactory = container.Resolve<MappingRegistryFactory>();
        }

        public void OnLoad()
        {
            oPage = new TabPage("Local Override");
            var presenter = container.Resolve<MappingEditorPresenter>();
            container.Resolve<LoggingPresenter>();
            var oEditor = (MappingEditor)presenter.View;
            logger = container.Resolve<ILogger>();
            oPage.Controls.Add(oEditor);
            var icon =
                new Icon(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(
                    "LocalOverride.Fiddler.override.ico"));
            const string imageKey = "Override";
            FiddlerApplication.UI.imglSessionIcons.Images.Add(imageKey, icon);
            var iconIndex = FiddlerApplication.UI.imglSessionIcons.Images.IndexOfKey(imageKey);
            oEditor.Dock = DockStyle.Fill;
            oPage.ImageIndex = iconIndex;
            FiddlerApplication.UI.tabsViews.TabPages.Add(oPage);
        }

        public void OnBeforeUnload()
        {
            container.Resolve<IMappingStore>().Persist();
        }

        public void AutoTamperRequestBefore(Session oSession)
        {
        }

        public void AutoTamperRequestAfter(Session oSession)
        {

        }

        public void AutoTamperResponseBefore(Session oSession)
        {
            var ignoreResponseCodes = new List<int> {401, 301, 307, 302, 303};
            if (!ignoreResponseCodes.Contains(oSession.responseCode))
            {
                var overrideMapping = mappingRegistryFactory.Create();
                var requestUri = new Uri(oSession.fullUrl);
                var mapping = overrideMapping.GetMapping(requestUri);

                if (mapping != MappingResult.NoMapping)
                {
                    logger.Log(String.Format("Mapping: {0} to {1}", requestUri, mapping.LocalFile));

                    OverrideResponse(oSession, mapping);
                }
            }
        }

        private void OverrideResponse(Session oSession, MappingResult mappingResult)
        {
            var localFile = mappingResult.LocalFile;
            if (File.Exists(localFile))
            {
                SendResponseFromFile(localFile, oSession);
            }
            else
            {
                if (mappingResult.DownloadMissingFile)
                {
                    if (SaveFile(oSession, localFile))
                        SendResponseFromFile(localFile, oSession);
                }
                else
                {
                    Return404Result(localFile, oSession);
                }
            }
        }

        private void SendResponseFromFile(string localFile, Session oSession)
        {
            oSession.LoadResponseFromFile(localFile);
            
            if(Path.GetExtension(localFile) == ".aspx")
                oSession.oResponse.headers["Content-Type"] = "text/html";
        }

        private bool SaveFile(Session session, string localFile)
        {
            logger.Log(String.Format("Could not find: {0} downloading", localFile));

            try
            {
                session.utilDecodeResponse();
                
                if (session.responseBodyBytes.Length == 0)
                    return false;

                WriteFile(localFile, session.responseBodyBytes);
            }
            catch (Exception ex)
            {
                logger.Log(String.Format("Exception while saving: {0} ", localFile));
                logger.Log(ex.ToString());
            }

            return true;
        }

        private void WriteFile(string localFile, byte[] fileBody)
        {
            EnsureDirectory(localFile);

            using (var file = File.Create(localFile))
            {
                file.Write(fileBody, 0, fileBody.Length);
                file.Close();
            }
        }

        private void EnsureDirectory(string localFile)
        {
            var directory = Path.GetDirectoryName(localFile);
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }
        }

        private void Return404Result(string localFile, Session oSession)
        {
            logger.Log(String.Format("Could not find: {0} returning 404", localFile));
            oSession.responseCode = 404;
            oSession.utilSetResponseBody("Could not find: 404");
        }

        public void AutoTamperResponseAfter(Session oSession)
        {
        }

        public void OnBeforeReturningError(Session oSession)
        {
        }
    }
}